package parallels.servertech.client;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;

import parallels.servertech.common.Receiver;
import parallels.servertech.common.Status;

public class DefaultCommandContext implements CommandContext {
	private final PrintStream out;
	private	Socket socket;
	private DataOutputStream output;
	private DataInputStream input;
	
	public DefaultCommandContext(PrintStream out) {
		this.out = out;
	}

	@Override
	public PrintStream getOut() {
		return out;
	}

	@Override
	public void connect(Socket socket) throws IOException {
		this.socket = socket;
		input = new DataInputStream(socket.getInputStream());
		output = new DataOutputStream(socket.getOutputStream());
	}
	
	@Override
	public void disconnect() throws IOException {
		out.println("disconnecting...");
		socket.shutdownOutput();
		out.println("client endpoint closed");
		out.println("waiting for server endpoint close...");
		
		try {
			while (true) {
				// TODO add timeout handling
				input.readByte();
			}
		} catch (EOFException eofEx) {
			out.println("server endpoint closed");
		}
		
		input.close();
	}

	@Override
	public Status send(String cmd, int... args) throws IOException {
		getOut().printf("sending %s command to server%n", cmd);
		output.writeUTF(cmd);
		for (int arg : args) {
			output.writeInt(arg);
		}
		getOut().println("receiving server reply");
		int statusCode = input.readInt();
		return Status.values()[statusCode];
	}

	@Override
	public <T> T receive(Receiver<T> receiver) throws IOException {
		return receiver.receive(input);
	}
	
	private static class ErrorReceiver implements Receiver<String> {
		@Override
		public String receive(DataInputStream input) throws IOException {
			return input.readUTF();
		}
	}

	@Override
	public String receiveError() throws IOException {
		return receive(new ErrorReceiver());
	}
}
